(* 
  Implementation of actual compiler that builds an environment
   of all variables, and does the conversion from Ast to Astc 
   and then prints out the Astc tree as c++ code
   Author: Esther Kundin & Ayla Brayer
*)

open Astconverter
module StringMap = Map.Make(String)


(* 
function add_type - takes a var_decl and adds the type
                    to a types map 
*)
let add_type types = function
    Ast.IntDeclaration (t, name, v) -> 
       if (StringMap.mem name types) then 
          raise (Failure ("multiple decleration of variable: " ^ name)) 
	   else 
	      StringMap.add name t types
  | Ast.FloatDeclaration (t, name, v) ->
     if (StringMap.mem name types) then 
        raise (Failure ("multiple decleration of variable: " ^ name)) 
     else 
        StringMap.add name t types
  | Ast.StringDeclaration (t, name, v) ->
     if (StringMap.mem name types) then 
	    raise (Failure ("multiple decleration of variable: " ^ name)) 
	 else 
	    StringMap.add name t types
  | Ast.BoolDeclaration (t, name, v) ->
     if (StringMap.mem name types) then 
	    raise (Failure ("multiple decleration of variable: " ^ name)) 
	 else 
	    StringMap.add name t types

(* 
function add_values - takes a var_decl and adds the value
 to a values map 
*)
let add_values values = function
    Ast.IntDeclaration (t, name, v) ->
        StringMap.add name (string_of_int v) values
  | Ast.FloatDeclaration (t, name, v) ->
	StringMap.add name (string_of_float v) values
  | Ast.StringDeclaration (t, name, v) ->
	StringMap.add name v values
  | Ast.BoolDeclaration (t, name, v) ->
	StringMap.add name (string_of_bool v) values



(* 
function check_name -
   here we hard-code all the keywords to check 
   variable names against 
   All we actually need here is to check for the
   'response' name, all others are already taken care of
   by the parser. With that said, it works and it's too 
   late to chance it. 
*)
let check_name name = if
   ((String.compare name "default") == 0) or
   ((String.compare name "repeat") == 0) or
   ((String.compare name "response") == 0) or
   ((String.compare name "print") == 0) or
   ((String.compare name "int") == 0) or
   ((String.compare name "float") == 0) or
   ((String.compare name "bool") == 0) or
   ((String.compare name "string") == 0) or
   ((String.compare name "true") == 0) or
   ((String.compare name "false") == 0)  then
   raise (Failure ("Cannot use a keyword as a variable name: " ^ name))
   else ()

(* checks the names of variables to make sure it isn't using keywords *)
let check_variable_names = function
    Ast.IntDeclaration (t, name, v) -> check_name name
  | Ast.FloatDeclaration (t, name, v) -> check_name name
  | Ast.StringDeclaration (t, name, v) -> check_name name
  | Ast.BoolDeclaration (t, name, v) -> check_name name

(* function compile - compiles Ast.program *)
let compile (var_decl, questions, statements) =
    List.iter check_variable_names var_decl;
    (* get a list of all types declared *)
    let types = List.fold_left add_type StringMap.empty var_decl in
    (* get a list of all values declared *)
    let values = List.fold_left add_values StringMap.empty var_decl
    in
    (* create a scope that has all the types and values for all
    variable names *)
    let myenv = {Astc.variable_types = types;
                 Astc.variable_values = values;
                 Astc.response_type = "string";
                 Astc.response_value = "garbage" } 
    in
    (* for debugging, print them out print_env myenv *)
      let ccode = Astc.string_of_cprogram (
	     Astconverter.cprogram_of_program(List.rev questions, 
		                                  List.rev statements, 
										  myenv), 
		 myenv)
	   in print_string ccode 

